#include <stdio.h>
#include <stdint.h>
#include <signal.h>    /*to catch CTRL+C*/
#include <unistd.h>
#include <stdlib.h>
/*VPROC SDK mandatory includes*/
#include "typedefs.h"
#include "chip.h"
#include "hbi.h"

//#define VPD1_DEVICE_ID 0 /*device ID of that Timberwolf*/
#define ZL380XX_EVENT_REG 0x0000
#define ZL380XX_GPIO_DIR_REG 0x2DC
#define ZL380XX_GPIO_WR_REG 0x2DA
#define ZL380XX_GPIO_DR_STRENGTH_REG 0x2DE
#define ZL380XX_GPIO_SPECIAL_REG 0x2D8
#define ZL380XX_GRAMMAR_LOADED_REG 0x050
#define ZL380XX_FWR_VER_REG	0x022
/*This apps is only supported for this ZL firmware*/
#define ZL380xx_ASR_FIRMWARE_OPN 38067  

//#define ZL380XX_GPIO_LED_CTRL	(1<<12)

#define MSCC_ASR_EVENT_INDICATOR_BIT    14
#define MSCC_ASR_TRIGGER_INDICATOR_BIT  13
#define MSCC_ASR_PHRASE_INDICATOR_BIT  12

#define GPIO_HI	1
#define GPIO_LO	0
/*ASR event 0x4000-0x7FFF
bit 14 set indicates ASR event
bit 13 set indicates event is a trigger, clear a command
bits 12:0 the detected phrase
*/
uint8_t zlledOnFlag = 0;
int flag_tsk1 = 1;
hbi_handle_t handle;
uint16_t ZL380XX_GPIO_LED_CTRL = (1<<12);
uint8_t VPD1_DEVICE_ID =0;

typedef enum
{
	APP_E_SENSOR_ASR_LIGHT_NONE = 0,
	APP_E_SENSOR_ASR_LIGHT_ON,
    APP_E_SENSOR_ASR_LIGHT_TURN_ON,
    APP_E_SENSOR_ASR_LIGHT_SMART,
    APP_E_SENSOR_ASR_LIGHT_BRIGHTER,
    APP_E_SENSOR_ASR_LIGHT_OFF,
    APP_E_SENSOR_ASR_LIGHT_TURN_OFF,
    APP_E_SENSOR_ASR_LIGHT_DECREASE,
    APP_E_SENSOR_ASR_LIGHT_UP,
    APP_E_SENSOR_ASR_LIGHT_STOP,
    APP_E_SENSOR_ASR_LIGHT_SWITCH,
    APP_E_SENSOR_ASR_LIGHT_FADE,
    APP_E_SENSOR_ASR_LIGHT_DOWN,
    APP_E_SENSOR_ASR_LIGHT_DIM,
    APP_E_SENSOR_ASR_LIGHT_DIMMER,
    APP_E_SENSOR_ASR_LIGHT_INCREASE,
    APP_E_SENSOR_ASR_LIGHT_FADER,
} APP_teSensorAsrCMD;


/* To stop the execution of the apps - it monitor for when
 * user presses CTRL+C
 */
void sigfun(int sig)
{
    printf("You have pressed Ctrl-C , exiting...");
    flag_tsk1 = 0;
    fflush(stdout);
}

static int zlAsrGrammarChk(void) {
    user_buffer_t buf_tx[4], buf_rx[4];
	reg_addr_t reg = ZL380XX_FWR_VER_REG;
	hbi_status_t status = HBI_STATUS_SUCCESS;
	uint16_t u16Temp = 0;

	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return -1;		
	}
	u16Temp = (buf_rx[0]<<8) | buf_rx[1];
	if (u16Temp != ZL380xx_ASR_FIRMWARE_OPN) {
		printf("the device is running firmware %d, this ASR demo apps is only supported for device %d. Exiting... \n", u16Temp, ZL380xx_ASR_FIRMWARE_OPN);
		 HBI_close(handle);
		return -1;
	}
	printf("the device is running firmware %d, checking for a compatible grammar..., \n", u16Temp);
	
	reg = ZL380xx_CUR_LOADED_FW_IMG_REG;
	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return -1;		
	}
	
	u16Temp = buf_rx[0]>>1;	
	if ( u16Temp == 0) {
       printf("This feature is only supported on firmware %d.1 or %d.2 Exiting...\n", ZL380xx_ASR_FIRMWARE_OPN);
	   HBI_close(handle);
	   return -1;
	}
	
	reg = ZL380XX_GRAMMAR_LOADED_REG;
	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return -1;		
	}
	u16Temp = (buf_rx[0]<<8) | buf_rx[1];
	if (u16Temp <= 0) {
		printf("No ASR Grammar is currently loaded. A compatible grammar must be programmed in the device. Exiting...\n");
		HBI_close(handle);
		return -1;
	}
	printf("the device is running grammar %d, system ready for ASR commands..., \n", u16Temp);

}

static void gpioLoHiCtrl(uint8_t data) {
    user_buffer_t buf_tx[4], buf_rx[4];
	reg_addr_t reg = ZL380XX_GPIO_WR_REG;
	hbi_status_t status = HBI_STATUS_SUCCESS;
	
	   /*Read GPIO direction*/ 
	reg = ZL380XX_GPIO_WR_REG;
	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
			printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return;
		
	}
	
	if (data == GPIO_HI) {
		buf_tx[0] = (ZL380XX_GPIO_LED_CTRL >> 8) | buf_rx[0];
		buf_tx[1] = (ZL380XX_GPIO_LED_CTRL & 0xFF) | buf_rx[1];
	} else {
		buf_tx[0] = buf_rx[0] & (~(ZL380XX_GPIO_LED_CTRL >> 8));
		buf_tx[1] = buf_rx[1] & (~(ZL380XX_GPIO_LED_CTRL & 0xFF));
	}
	printf("GPIO_WR: 0x%04x \n", ((buf_tx[0]<<8) | buf_tx[1]));
	status  = HBI_write(handle,  reg, buf_tx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_write() failed\n", status);
		HBI_close(handle);
		return;
	}
}

static void vMsccDevProcessASR(uint8_t u8asrEventType, uint8_t u8asrTrigger, uint16_t u16asrIndicator, uint16_t u16asrEventData)
{
	if (u8asrTrigger)
	{
		printf("[ASR]: Trigger ... ASR detected, Confidence = 0x%04x...\n", u16asrEventData);
	}

	if ((u8asrEventType) && (u16asrIndicator))
	{
		printf("[ASR]: Command ... ASR Indicator Phrase = 0x%04x detected, Confidence  = 0x%04x...\n", u16asrIndicator, u16asrEventData);
		switch(u16asrIndicator) {
			case APP_E_SENSOR_ASR_LIGHT_SWITCH:
				if(zlledOnFlag) {
					zlledOnFlag = 0;
					gpioLoHiCtrl(zlledOnFlag);
					printf("[ASR]: Light switch off....\n");
				} else {
					zlledOnFlag = 1;
					gpioLoHiCtrl(zlledOnFlag);
					printf("[ASR]: Light switch on....\n");
				}
				break;
			case APP_E_SENSOR_ASR_LIGHT_ON:
			case APP_E_SENSOR_ASR_LIGHT_TURN_ON:
				zlledOnFlag = 1;
				gpioLoHiCtrl(zlledOnFlag);
				printf("[ASR]: Light On....\n");
				break;
			case APP_E_SENSOR_ASR_LIGHT_TURN_OFF:
			case APP_E_SENSOR_ASR_LIGHT_OFF:
			case APP_E_SENSOR_ASR_LIGHT_STOP:
				zlledOnFlag = 0;
				gpioLoHiCtrl(zlledOnFlag);
				printf("[ASR]: Light Off....\n");
				break;
			case APP_E_SENSOR_ASR_LIGHT_DECREASE:
			case APP_E_SENSOR_ASR_LIGHT_DOWN:
			case APP_E_SENSOR_ASR_LIGHT_DIM:
			case APP_E_SENSOR_ASR_LIGHT_DIMMER:
			case APP_E_SENSOR_ASR_LIGHT_FADE:
			case APP_E_SENSOR_ASR_LIGHT_FADER:
                printf("[ASR]: Light dimming....\n");
				break;
			case APP_E_SENSOR_ASR_LIGHT_BRIGHTER:
			case APP_E_SENSOR_ASR_LIGHT_INCREASE:
			case APP_E_SENSOR_ASR_LIGHT_UP:
			{
				printf("[ASR]: Light brighter....\n");
				break;
			}
			case APP_E_SENSOR_ASR_LIGHT_SMART:
				zlledOnFlag = 1;
				gpioLoHiCtrl(zlledOnFlag);
				printf("[ASR]: Light smart....\n");
				break;
			default:
				break;
		}		
	}
	if ((u8asrEventType) && (u16asrIndicator == 0) && (u8asrTrigger == 0))
	{
		printf("[ASR]: Timeout ... ASR timeout, Confidence  = 0x%04x...\n", u16asrEventData);
	}
	
}

static void usage(void)
{
   printf("\nPurpose: To drive a desired ZL GPIO via ASR commands\n"); 
   printf("-i: the GPIO number to driver range [0 to 13] \n");
   printf("-d: the device id n (0.....255) \n");
   printf("Example to use GPIO 12 on device 1: hbi_asr_gpio_ctrl -d 1 -i 12 \n");
   printf("\nNOTE: Execution can be interrupted at any time by pressing CTRL+C keys\n");   
   return;
}


void main (int argc, char** argv)
{
    hbi_dev_cfg_t devcfg;
    
    reg_addr_t reg = 0x00E;
    user_buffer_t buf_tx[4];
    user_buffer_t buf_rx[4];
    hbi_status_t status = HBI_STATUS_SUCCESS; 
    int i = 0, c;
	uint16_t u16EventId = 0;
	uint16_t u16EventData = 0;
    uint8_t u8asrEventType = 0;
    uint8_t u8asrTrigger   = 0;
    uint16_t u16asrIndicator = 0;
	uint8_t SLCT_GPIO = 0;
	
	while((c = getopt(argc,argv,"d:i:h")) != -1)
	{
	    switch(c)
	    {
			 case 'i':
				SLCT_GPIO= ((uint8_t)strtoul(optarg,NULL,0));
			 break;          
			 case 'd':
				VPD1_DEVICE_ID= ((uint8_t)strtoul(optarg,NULL,0));
			 break;          
			 case 'h':
				usage();
			 return;
			 default:
				printf("GPIO number Argument Missing: Specify which GPIO to control \n");
			 return;
	    }
    }
    printf("GPIO %d will be controlled on ZL380xx device at CS %d\n", SLCT_GPIO, VPD1_DEVICE_ID);
    ZL380XX_GPIO_LED_CTRL = (1<<SLCT_GPIO);
    devcfg.deviceId = VPD1_DEVICE_ID;
    devcfg.pDevName = NULL;
    /*Open one instance of the Timberwolf identified by VPD1_DEVICE_ID */ 
    status = HBI_open(&handle,&devcfg);
    if(status != HBI_STATUS_SUCCESS)
    {
         printf("HBI ERROR: %d, HBI_open() \n", status);
         return;
    }
	
	if (zlAsrGrammarChk() < 0)
	   return;
	
   /*Read GPIO direction*/ 
	reg = ZL380XX_GPIO_DIR_REG;
	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
			printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return;
		
	}
   /*Write GPIO direction to output*/	
	buf_tx[0] = (ZL380XX_GPIO_LED_CTRL >> 8) | buf_rx[0];
	buf_tx[1] = (ZL380XX_GPIO_LED_CTRL & 0xFF) | buf_rx[1];
	reg = ZL380XX_GPIO_DIR_REG;
	status  = HBI_write(handle,  reg, buf_tx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_write() failed\n", status);
		HBI_close(handle);
		return;
	}

	   /*Read GPIO direction*/ 
	reg = ZL380XX_GPIO_WR_REG;
	status  = HBI_read(handle, reg, buf_rx, 2);
	if (status != HBI_STATUS_SUCCESS) {
			printf("HBI ERROR: %d, HBI_read() failed\n", status);
		HBI_close(handle);
		return;
		
	}

	/*Write GPIO low */
	buf_tx[0] = buf_rx[0] & (~(ZL380XX_GPIO_LED_CTRL >> 8));
	buf_tx[1] = buf_rx[1] & (~(ZL380XX_GPIO_LED_CTRL & 0xFF));
	reg = ZL380XX_GPIO_WR_REG;
   /*Write the content of buf into the Timberwolf register defined in reg*/
	status  = HBI_write(handle,  reg, buf_tx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_write() failed\n", status);
		HBI_close(handle);
		return;
	}

	buf_tx[0] = 0x03;
	buf_tx[1] = 0x00;
	reg = ZL380XX_GPIO_DR_STRENGTH_REG;
   /*Write the content of buf into the Timberwolf register defined in reg*/
	status  = HBI_write(handle,  reg, buf_tx, 2);
	if (status != HBI_STATUS_SUCCESS) {
		printf("HBI ERROR: %d, HBI_write() failed\n", status);
		HBI_close(handle);
		return;
	}
	(void) signal(SIGINT, sigfun);
	while(flag_tsk1) {

	   /*Read the Timberwolf register defined in reg and store the result in buf*/ 
	    reg = ZL380XX_EVENT_REG;
		status  = HBI_read(handle, reg, buf_rx, 4);
		if (status != HBI_STATUS_SUCCESS) {
			printf("HBI ERROR: %d, HBI_read() failed\n", status);
			HBI_close(handle);
			return;			
		}
		u16EventId = (buf_rx[0]<<8) | buf_rx[1];
		u16EventData = (buf_rx[2]<<8) | buf_rx[3];
		
		u8asrEventType = ((u16EventId & (1 << MSCC_ASR_EVENT_INDICATOR_BIT))>>MSCC_ASR_EVENT_INDICATOR_BIT);
		u8asrTrigger   = ((u16EventId & (1 << MSCC_ASR_TRIGGER_INDICATOR_BIT))>>MSCC_ASR_TRIGGER_INDICATOR_BIT);
		u16asrIndicator = u16EventId & ((1 << MSCC_ASR_PHRASE_INDICATOR_BIT)-1);
		
		if ((u8asrEventType) || (u8asrTrigger )) {
		    printf("eventID = 0x%04x \t eventData = 0x%04x\n", u16EventId, u16EventData);
		    vMsccDevProcessASR(u8asrEventType, u8asrTrigger, u16asrIndicator, u16EventData);
		}
		usleep(5000);
	}
    /*Close the opened instance*/
    HBI_close(handle);
    return; 
}
